﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace OCRTheNewHope
{
    public class Digit
    {
        public int DigitID { get; private set; }
        public byte DigitValue { get; private set; }

        public byte[,] OriginalGrayscaleMatrix { get; private set; }
        public float[,] IntegralMatrix { get; private set; }

        public Digit(int digitID, byte digitValue, byte[,] originalMatrix)
        {
            this.DigitID = digitID;
            this.DigitValue = digitValue;
            this.OriginalGrayscaleMatrix = originalMatrix;

            this.UpdateIntegralMatrix();
        }

        public Digit(int digitID, byte digitValue, byte[,] originalMatrix, float[,] integralMatrix)
        {
            this.DigitID = digitID;
            this.DigitValue = digitValue;
            this.OriginalGrayscaleMatrix = originalMatrix;

            this.IntegralMatrix = integralMatrix;
        }

        private void UpdateIntegralMatrix()
        {
            this.IntegralMatrix = new float[this.OriginalGrayscaleMatrix.GetLength(0), this.OriginalGrayscaleMatrix.GetLength(1)];

            for (int i = 0; i < this.OriginalGrayscaleMatrix.GetLength(0); i++)
            {
                this.IntegralMatrix[i, 0] = this.OriginalGrayscaleMatrix[i, 0];
            }

            for (int i = 0; i < this.OriginalGrayscaleMatrix.GetLength(0); i++)
            {
                for (int j = 1; j < this.OriginalGrayscaleMatrix.GetLength(1); j++)
                {
                    this.IntegralMatrix[i, j] = this.IntegralMatrix[i, j - 1] + (float)this.OriginalGrayscaleMatrix[i, j];
                }
            }

            for (int j = 0; j < this.OriginalGrayscaleMatrix.GetLength(1); j++)
            {
                for (int i = 1; i < this.OriginalGrayscaleMatrix.GetLength(0); i++)
                {
                    this.IntegralMatrix[i, j] = this.IntegralMatrix[i - 1, j] + this.IntegralMatrix[i, j];
                }
            }

            float scalar = 1 / this.IntegralMatrix[this.OriginalGrayscaleMatrix.GetLength(0) - 1, this.OriginalGrayscaleMatrix.GetLength(1) - 1];

            for (int i = 0; i < this.OriginalGrayscaleMatrix.GetLength(0); i++)
            {
                for (int j = 0; j < this.OriginalGrayscaleMatrix.GetLength(1); j++)
                {
                    this.IntegralMatrix[i, j] = this.IntegralMatrix[i, j] * scalar;
                }
            }
        }

        public Image GetGrayscaleImage()
        {
            Bitmap bmp = new Bitmap(this.OriginalGrayscaleMatrix.GetLength(0), this.OriginalGrayscaleMatrix.GetLength(1));

            for (int i = 0; i < this.OriginalGrayscaleMatrix.GetLength(0); i++)
            {
                for (int j = 0; j < this.OriginalGrayscaleMatrix.GetLength(1); j++)
                {
                    int temp = 255 - this.OriginalGrayscaleMatrix[i, j];
                    bmp.SetPixel(j, i, Color.FromArgb(temp, temp, temp));
                }
            }

            return bmp;
        }

        public double ComputeRectangleSum(Rectangle rect)
        {
            double A = this.IntegralMatrix[rect.A.Y, rect.A.X];
            double B = this.IntegralMatrix[rect.B.Y, rect.B.X];
            double C = this.IntegralMatrix[rect.C.Y, rect.C.X];
            double D = this.IntegralMatrix[rect.D.Y, rect.D.X];

            double rVal = D + A - B - C;

            return rVal;
        }

        public bool[,] ComputeFeature(Feature f)
        {
            bool[,] rVal = new bool[2, Parameters.NumberOfThresholds];

            double recSum0;
            double recSum1;

            recSum0 = ComputeRectangleSum(f.Rectangle0);
            recSum1 = ComputeRectangleSum(f.Rectangle1);

            double diff = recSum0 - recSum1;
            for (int idx = 0; idx < Parameters.NumberOfThresholds; idx++)
            {
                if (diff < Feature.ThresholdCandidates[0, idx])
                    rVal[0, idx] = false;
                else
                    rVal[0, idx] = true;
            }
            
            double sum = (recSum0 + recSum1) / 2.0;
            for (int idx = 0; idx < Parameters.NumberOfThresholds; idx++)
            {
                if (sum < Feature.ThresholdCandidates[1, idx])
                    rVal[1, idx] = false;
                else
                    rVal[1, idx] = true;
            }
            return rVal;
        }

        public bool ComputeTestFeature(Feature feature)
        {
            bool rVal;

            double recSum0;
            double recSum1;
            recSum0 = ComputeRectangleSum(feature.Rectangle0);
            recSum1 = ComputeRectangleSum(feature.Rectangle1);

            double temp;
            if (feature.FeatureType == FeatureType.MINUS)
            {
                temp = recSum0 - recSum1;
            }
            else
            {
                temp = (recSum0 + recSum1) / 2;
            }

            if (temp < feature.Threshold)
            {
                rVal = false;
            }
            else
            {
                rVal = true;
            }

            return rVal;
        }

        public override string ToString()
        {
            return "Digit: " + this.DigitValue.ToString();
        }
    }
}
